/***********************************************************************************
 * 文件名： hc32_adv_timer.h
 * 作者： 刘言
 * 版本： 1
 * 说明：
 * 		HC32系列的高级定时器驱动，功能较多，不断完善中。
 *      中断入口统一定义在 isr.c 中。
 * 修改记录：
 * 	2020/10/26: 初版。 刘言。
***********************************************************************************/

#ifndef _HC32_ADV_TIMER_H_
#define _HC32_ADV_TIMER_H_

#include "HC32_LyLib.h"

#ifdef __cplusplus
 extern "C" {
#endif 

// 预处理

#if (TIM4_CHA_MODE == 1)
#define TIM4_CHA_EN_BUFF    0       // 1-启用缓存，仅在计数周期结束时更新新的值，0-不启用。捕获输入模式时不要启用。
#else
#define TIM4_CHA_EN_BUFF    1
#endif

#if (TIM4_CHA_MODE == 1)||(TIM4_DTCEN == 1)
#define TIM4_CHB_EN_BUFF    0       // 1-启用缓存，仅在计数周期结束时更新新的比较值，0-不启用 （捕获输入模式时不要启用。互补输出时启用死区设置后，该值应保持0）
#else
#define TIM4_CHB_EN_BUFF    1
#endif

#if (TIM5_CHA_MODE == 1)
#define TIM5_CHA_EN_BUFF    0       // 1-启用缓存，仅在计数周期结束时更新新的值，0-不启用。捕获输入模式时不要启用。
#else
#define TIM5_CHA_EN_BUFF    1
#endif

#if (TIM5_CHA_MODE == 1)||(TIM5_DTCEN == 1)
#define TIM5_CHB_EN_BUFF    0       // 1-启用缓存，仅在计数周期结束时更新新的比较值，0-不启用 （捕获输入模式时不要启用。互补输出时启用死区设置后，该值应保持0）
#else
#define TIM5_CHB_EN_BUFF    1
#endif

#if (TIM6_CHA_MODE == 1)
#define TIM6_CHA_EN_BUFF    0       // 1-启用缓存，仅在计数周期结束时更新新的值，0-不启用。捕获输入模式时不要启用。
#else
#define TIM6_CHA_EN_BUFF    1
#endif

#if (TIM6_CHA_MODE == 1)||(TIM6_DTCEN == 1)
#define TIM6_CHB_EN_BUFF    0       // 1-启用缓存，仅在计数周期结束时更新新的比较值，0-不启用 （捕获输入模式时不要启用。互补输出时启用死区设置后，该值应保持0）
#else
#define TIM6_CHB_EN_BUFF    1
#endif


// 类型定义

// 状态标志定义
typedef enum _advtim_stf
{
    ADVTIM_STF_CMA = (1 << 0),    // 计数匹配A（或捕获输入A）状态
    ADVTIM_STF_CMB = (1 << 1),    // 计数匹配B（或捕获输入B）状态
    ADVTIM_STF_CMC = (1 << 2),    // 计数匹配C状态
    ADVTIM_STF_CMD = (1 << 3),    // 计数匹配D状态
    ADVTIM_STF_OVF = (1 << 6),    // 向上溢出状态
    ADVTIM_STF_UDF = (1 << 7),    // 向下溢出状态
    ADVTIM_STF_DTE = (1 << 8),    // 死区错误状态
    ADVTIM_STF_DIRF = (1 << 31),  // 计数方向
    ADVTIM_STF_ALL = 0XFFFFFFFF
}advtim_stf_t;

// 中断请求标志定义
typedef enum _advtim_irqf
{
    ADVTIM_IRQF_CMA = (1 << 0),    // 计数匹配A（或捕获输入A）中断
    ADVTIM_IRQF_CMB = (1 << 1),    // 计数匹配B（或捕获输入B）中断
    ADVTIM_IRQF_CMC = (1 << 2),    // 计数匹配C中断
    ADVTIM_IRQF_CMD = (1 << 3),    // 计数匹配D中断
    ADVTIM_IRQF_OVF = (1 << 6),    // 向上溢出中断
    ADVTIM_IRQF_UDF = (1 << 7),    // 向下溢出中断
    ADVTIM_IRQF_DTE = (1 << 8),    // 死区错误中断
    ADVTIM_IRQF_SAML = (1 << 14),  // 互补输出AB同低中断
    ADVTIM_IRQF_SAMH = (1 << 15),  // 互补输出AB同高中断
    ADVTIM_IRQF_ALL = 0XFFFFFFFF
}advtim_irqf_t;

// ADVTIM硬件(启动/停止/清零/捕获)事件触发源
typedef enum _advtim_hw_trig
{
    ADVTIM_HW_TRIG_CHA_RISE = (1 << 4), // CHA管脚检测到上升沿
    ADVTIM_HW_TRIG_CHA_FALL = (1 << 5), // CHA管脚检测到下降沿
    ADVTIM_HW_TRIG_CHB_RISE = (1 << 6), // CHB管脚检测到上升沿
    ADVTIM_HW_TRIG_CHB_FALL = (1 << 7), // CHB管脚检测到下降沿
    _ADVTIM_HW_TRIG_END
}advtim_hw_trig_t;


// APIs

// 按照配置文件的初始值初始化TIM4
void AdvTim_Tim4Init();

// 按照配置文件的初始值初始化TIM5
void AdvTim_Tim5Init();

// 按照配置文件的初始值初始化TIM6
void AdvTim_Tim6Init();

STATIC_FORCE_IN_LINE void AdvTim_Run(M0P_ADTIM_TypeDef *M0P_ADTIMx)
{
    M0P_ADTIMx->GCONR_f.START = 1;
}

STATIC_FORCE_IN_LINE void AdvTim_Stop(M0P_ADTIM_TypeDef *M0P_ADTIMx)
{
    M0P_ADTIMx->GCONR_f.START = 0;
}

// 清零计数寄存器
STATIC_FORCE_IN_LINE void AdvTim_ClrCount(M0P_ADTIM_TypeDef *M0P_ADTIMx)
{
    M0P_ADTIMx->CNTER = 0;
}

// 获取状态标志
// M0P_ADTIMx: 高级定时器（寄存器）对象指针。
// status: 哪个状态，取值见 advtim_stf_t 。多个状态可用 | 连接，只要其中一个状态有效就会返回true。
STATIC_FORCE_IN_LINE bool AdvTim_GetStatusFlag(M0P_ADTIM_TypeDef *M0P_ADTIMx, advtim_stf_t status)
{
    return ((M0P_ADTIMx->STFLR & (status)) > 0);
}

// 清除状态标志
// M0P_ADTIMx: 高级定时器（寄存器）对象指针。
// status: 哪个状态，取值见 advtim_stf_t 。多个状态可用 | 连接，一次清除。
STATIC_FORCE_IN_LINE void AdvTim_ClrStatusFlag(M0P_ADTIM_TypeDef *M0P_ADTIMx, advtim_stf_t status)
{
    M0P_ADTIMx->STFLR &= ~(status);
}

// 获取中断标志（是否发生中断请求）
// M0P_ADTIMx: 高级定时器（寄存器）对象指针。
// irq: 哪个中断，取值见 advtim_irqf_t 。多个中断请求可用 | 连接，只要其中一个标志有效就会返回true。
// 若没有开启对应中断将不会置位中断标志，但是可以获取对应的状态标志。
STATIC_FORCE_IN_LINE bool AdvTim_GetIrqFlag(M0P_ADTIM_TypeDef *M0P_ADTIMx, advtim_irqf_t irq)
{
    return ((M0P_ADTIMx->IFR & (irq)) > 0);
}

// 清除中断请求标志
// M0P_ADTIMx: 高级定时器（寄存器）对象指针。
// irq: 哪个中断，取值见 advtim_irqf_t 。多个中断请求可用 | 连接，一次清除。
STATIC_FORCE_IN_LINE void AdvTim_ClrIrqFlag(M0P_ADTIM_TypeDef *M0P_ADTIMx, advtim_irqf_t irq)
{
    M0P_ADTIMx->ICLR = ~(irq);
}

// 设置比较值。
// M0P_ADTIMx: 高级定时器（寄存器）对象指针。
// ch - 通道号，0-1 分别代表 A-B。
// value - 比较值。
// 注意，设置的仅仅是缓存值，将在计数上溢点、下溢点、谷点、锋点（具体可配置）时才会应用设置的值。
STATIC_FORCE_IN_LINE void AdvTim_SetCompValue(M0P_ADTIM_TypeDef *M0P_ADTIMx, int ch, int value)
{
    *(vu32 *)((u32)M0P_ADTIMx + 0x10 + 8 + ch * 4) = value;
    //*(vu32 *)(M0P_ADTIMx + 0x10 + ch * 4) = value;
}

// 设置指定通道的捕获事件。
// M0P_ADTIMx: 高级定时器（寄存器）对象指针。
// ch - 通道号，0~1 分别代表 A~B。
// event - 事件。取值见 advtim_hw_trig_t ，多个事件可用 | 连接。
// 注意: 执行后未被设置的事件将会关闭，不可以通过多次调用设置多个事件; 别忘记将对应通道设置为捕获输入模式。
STATIC_FORCE_IN_LINE void AdvTim_SetCapEvent(M0P_ADTIM_TypeDef *M0P_ADTIMx, int ch, advtim_hw_trig_t event)
{
    *(vu32 *)((u32)M0P_ADTIMx + 0x80 + ch * 4) = event;
}

// 获取指定通道的捕获值。
// M0P_ADTIMx: 高级定时器（寄存器）对象指针。
// ch - 通道号，0~1 分别代表 A~B。
STATIC_FORCE_IN_LINE u32 AdvTim_GetCapValue(M0P_ADTIM_TypeDef *M0P_ADTIMx, int ch)
{
    return *(vu32 *)((u32)M0P_ADTIMx + 0x10 + ch * 4);
}

// 同时启动TIM4 TIM5 TIM6
// sb - 要同时启动的定时器，最后3位有效，[2..0]分别代表 TIM6 TIM5 TIM4
STATIC_FORCE_IN_LINE void AdvTim_SyncRun(u16 sb)
{
    M0P_ADTIM4->SSTAR = sb;
}

// 同时停止TIM4 TIM5 TIM6
// sb - 要同时停止的定时器，最后3位有效，[2..0]分别代表 TIM6 TIM5 TIM4
STATIC_FORCE_IN_LINE void AdvTim_SyncStop(u16 sb)
{
    M0P_ADTIM4->SSTPR = sb;
}



#ifdef __cplusplus
}
#endif

#endif
